home *** CD-ROM | disk | FTP | other *** search
/ Freelog 115 / FreelogNo115-MaiJuin2013.iso / Internet / AvantBrowser / asetup.exe / _data / webkit / resources.pak / Unnamed File 000005.txt < prev    next >
Text File  |  2013-04-03  |  18KB  |  576 lines

  1. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4.  
  5. // TODO(arv): Now that this is driven by a data model, implement a data model
  6. //            that handles the loading and the events from the bookmark backend.
  7.  
  8. cr.define('bmm', function() {
  9.   var List = cr.ui.List;
  10.   var ListItem = cr.ui.ListItem;
  11.   var ArrayDataModel = cr.ui.ArrayDataModel;
  12.   var ContextMenuButton = cr.ui.ContextMenuButton;
  13.  
  14.   var list;
  15.  
  16.   /**
  17.    * Basic array data model for use with bookmarks.
  18.    * @param {!Array.<!BookmarkTreeNode>} items The bookmark items.
  19.    * @constructor
  20.    * @extends {ArrayDataModel}
  21.    */
  22.   function BookmarksArrayDataModel(items) {
  23.     ArrayDataModel.call(this, items);
  24.   }
  25.  
  26.   BookmarksArrayDataModel.prototype = {
  27.     __proto__: ArrayDataModel.prototype,
  28.  
  29.     /**
  30.      * Finds the index of the bookmark with the given ID.
  31.      * @param {string} id The ID of the bookmark node to find.
  32.      * @return {number} The index of the found node or -1 if not found.
  33.      */
  34.     findIndexById: function(id) {
  35.       for (var i = 0; i < this.length; i++) {
  36.         if (this.item(i).id == id)
  37.           return i;
  38.       }
  39.       return -1;
  40.     }
  41.   };
  42.  
  43.   /**
  44.    * Removes all children and appends a new child.
  45.    * @param {!Node} parent The node to remove all children from.
  46.    * @param {!Node} newChild The new child to append.
  47.    */
  48.   function replaceAllChildren(parent, newChild) {
  49.     var n;
  50.     while ((n = parent.lastChild)) {
  51.       parent.removeChild(n);
  52.     }
  53.     parent.appendChild(newChild);
  54.   }
  55.  
  56.   /**
  57.    * Creates a new bookmark list.
  58.    * @param {Object=} opt_propertyBag Optional properties.
  59.    * @constructor
  60.    * @extends {HTMLButtonElement}
  61.    */
  62.   var BookmarkList = cr.ui.define('list');
  63.  
  64.   BookmarkList.prototype = {
  65.     __proto__: List.prototype,
  66.  
  67.     /** @override */
  68.     decorate: function() {
  69.       List.prototype.decorate.call(this);
  70.       this.addEventListener('mousedown', this.handleMouseDown_);
  71.  
  72.       // HACK(arv): http://crbug.com/40902
  73.       window.addEventListener('resize', this.redraw.bind(this));
  74.  
  75.       // We could add the ContextMenuButton in the BookmarkListItem but it slows
  76.       // down redraws a lot so we do this on mouseovers instead.
  77.       this.addEventListener('mouseover', this.handleMouseOver_.bind(this));
  78.  
  79.       bmm.list = this;
  80.     },
  81.  
  82.     createItem: function(bookmarkNode) {
  83.       return new BookmarkListItem(bookmarkNode);
  84.     },
  85.  
  86.     parentId_: '',
  87.  
  88.     /**
  89.      * Reloads the list from the bookmarks backend.
  90.      */
  91.     reload: function() {
  92.       var parentId = this.parentId;
  93.  
  94.       var callback = this.handleBookmarkCallback_.bind(this);
  95.       this.loading_ = true;
  96.  
  97.       if (!parentId) {
  98.         callback([]);
  99.       } else if (/^q=/.test(parentId)) {
  100.         chrome.bookmarks.search(parentId.slice(2), callback);
  101.       } else if (parentId == 'recent') {
  102.         chrome.bookmarks.getRecent(50, callback);
  103.       } else {
  104.         chrome.bookmarks.getChildren(parentId, callback);
  105.       }
  106.     },
  107.  
  108.     /**
  109.      * Callback function for loading items.
  110.      * @param {Array.<!BookmarkTreeNode>} items The loaded items.
  111.      * @private
  112.      */
  113.     handleBookmarkCallback_: function(items) {
  114.       if (!items) {
  115.         // Failed to load bookmarks. Most likely due to the bookmark being
  116.         // removed.
  117.         cr.dispatchSimpleEvent(this, 'invalidId');
  118.         this.loading_ = false;
  119.         return;
  120.       }
  121.  
  122.       this.dataModel = new BookmarksArrayDataModel(items);
  123.  
  124.       this.loading_ = false;
  125.       this.fixWidth_();
  126.       cr.dispatchSimpleEvent(this, 'load');
  127.     },
  128.  
  129.     /**
  130.      * The bookmark node that the list is currently displaying. If we are
  131.      * currently displaying recent or search this returns null.
  132.      * @type {BookmarkTreeNode}
  133.      */
  134.     get bookmarkNode() {
  135.       if (this.isSearch() || this.isRecent())
  136.         return null;
  137.       var treeItem = bmm.treeLookup[this.parentId];
  138.       return treeItem && treeItem.bookmarkNode;
  139.     },
  140.  
  141.     /**
  142.      * @return {boolean} Whether we are currently showing search results.
  143.      */
  144.     isSearch: function() {
  145.       return this.parentId_[0] == 'q';
  146.     },
  147.  
  148.     /**
  149.      * @return {boolean} Whether we are currently showing recent bookmakrs.
  150.      */
  151.     isRecent: function() {
  152.       return this.parentId_ == 'recent';
  153.     },
  154.  
  155.     /**
  156.      * Handles mouseover on the list so that we can add the context menu button
  157.      * lazily.
  158.      * @private
  159.      * @param {!Event} e The mouseover event object.
  160.      */
  161.     handleMouseOver_: function(e) {
  162.       var el = e.target;
  163.       while (el && el.parentNode != this) {
  164.         el = el.parentNode;
  165.       }
  166.  
  167.       if (el && el.parentNode == this &&
  168.           !(el.lastChild instanceof ContextMenuButton)) {
  169.         el.appendChild(new ContextMenuButton);
  170.       }
  171.     },
  172.  
  173.     /**
  174.      * Dispatches an urlClicked event which is used to open URLs in new
  175.      * tabs etc.
  176.      * @private
  177.      * @param {string} url The URL that was clicked.
  178.      * @param {!Event} originalEvent The original click event object.
  179.      */
  180.     dispatchUrlClickedEvent_: function(url, originalEvent) {
  181.       var event = new cr.Event('urlClicked', true, false);
  182.       event.url = url;
  183.       event.originalEvent = originalEvent;
  184.       this.dispatchEvent(event);
  185.     },
  186.  
  187.     /**
  188.      * Handles mousedown events so that we can prevent the auto scroll as
  189.      * necessary.
  190.      * @private
  191.      * @param {!MouseEvent} e The mousedown event object.
  192.      */
  193.     handleMouseDown_: function(e) {
  194.       if (e.button == 1) {
  195.         // WebKit no longer fires click events for middle clicks so we manually
  196.         // listen to mouse up to dispatch a click event.
  197.         this.addEventListener('mouseup', this.handleMiddleMouseUp_);
  198.  
  199.         // When the user does a middle click we need to prevent the auto scroll
  200.         // in case the user is trying to middle click to open a bookmark in a
  201.         // background tab.
  202.         // We do not do this in case the target is an input since middle click
  203.         // is also paste on Linux and we don't want to break that.
  204.         if (e.target.tagName != 'INPUT')
  205.           e.preventDefault();
  206.       }
  207.     },
  208.  
  209.     /**
  210.      * WebKit no longer dispatches click events for middle clicks so we need
  211.      * to emulate it.
  212.      * @private
  213.      * @param {!MouseEvent} e The mouse up event object.
  214.      */
  215.     handleMiddleMouseUp_: function(e) {
  216.       this.removeEventListener('mouseup', this.handleMiddleMouseUp_);
  217.       if (e.button == 1) {
  218.         var el = e.target;
  219.         while (el.parentNode != this) {
  220.           el = el.parentNode;
  221.         }
  222.         var node = el.bookmarkNode;
  223.         if (node && !bmm.isFolder(node))
  224.           this.dispatchUrlClickedEvent_(node.url, e);
  225.       }
  226.     },
  227.  
  228.     // Bookmark model update callbacks
  229.     handleBookmarkChanged: function(id, changeInfo) {
  230.       var dataModel = this.dataModel;
  231.       var index = dataModel.findIndexById(id);
  232.       if (index != -1) {
  233.         var bookmarkNode = this.dataModel.item(index);
  234.         bookmarkNode.title = changeInfo.title;
  235.         if ('url' in changeInfo)
  236.           bookmarkNode.url = changeInfo['url'];
  237.  
  238.         dataModel.updateIndex(index);
  239.       }
  240.     },
  241.  
  242.     handleChildrenReordered: function(id, reorderInfo) {
  243.       if (this.parentId == id) {
  244.         // We create a new data model with updated items in the right order.
  245.         var dataModel = this.dataModel;
  246.         var items = {};
  247.         for (var i = this.dataModel.length - 1; i >= 0; i--) {
  248.           var bookmarkNode = dataModel.item(i);
  249.           items[bookmarkNode.id] = bookmarkNode;
  250.         }
  251.         var newArray = [];
  252.         for (var i = 0; i < reorderInfo.childIds.length; i++) {
  253.           newArray[i] = items[reorderInfo.childIds[i]];
  254.         }
  255.  
  256.         this.dataModel = new BookmarksArrayDataModel(newArray);
  257.       }
  258.     },
  259.  
  260.     handleCreated: function(id, bookmarkNode) {
  261.       if (this.parentId == bookmarkNode.parentId) {
  262.         this.dataModel.splice(bookmarkNode.index, 0, bookmarkNode);
  263.       }
  264.     },
  265.  
  266.     handleMoved: function(id, moveInfo) {
  267.       if (moveInfo.parentId == this.parentId ||
  268.           moveInfo.oldParentId == this.parentId) {
  269.  
  270.         var dataModel = this.dataModel;
  271.  
  272.         if (moveInfo.oldParentId == moveInfo.parentId) {
  273.           // Reorder within this folder
  274.  
  275.           this.startBatchUpdates();
  276.  
  277.           var bookmarkNode = this.dataModel.item(moveInfo.oldIndex);
  278.           this.dataModel.splice(moveInfo.oldIndex, 1);
  279.           this.dataModel.splice(moveInfo.index, 0, bookmarkNode);
  280.  
  281.           this.endBatchUpdates();
  282.         } else {
  283.           if (moveInfo.oldParentId == this.parentId) {
  284.             // Move out of this folder
  285.  
  286.             var index = dataModel.findIndexById(id);
  287.             if (index != -1)
  288.               dataModel.splice(index, 1);
  289.           }
  290.  
  291.           if (moveInfo.parentId == this.parentId) {
  292.             // Move to this folder
  293.             var self = this;
  294.             chrome.bookmarks.get(id, function(bookmarkNodes) {
  295.               var bookmarkNode = bookmarkNodes[0];
  296.               dataModel.splice(bookmarkNode.index, 0, bookmarkNode);
  297.             });
  298.           }
  299.         }
  300.       }
  301.     },
  302.  
  303.     handleRemoved: function(id, removeInfo) {
  304.       var dataModel = this.dataModel;
  305.       var index = dataModel.findIndexById(id);
  306.       if (index != -1)
  307.         dataModel.splice(index, 1);
  308.     },
  309.  
  310.     /**
  311.      * Workaround for http://crbug.com/40902
  312.      * @private
  313.      */
  314.     fixWidth_: function() {
  315.       var list = bmm.list;
  316.       if (this.loading_ || !list)
  317.         return;
  318.  
  319.       // The width of the list is wrong after its content has changed.
  320.       // Fortunately the reported offsetWidth is correct so we can detect the
  321.       //incorrect width.
  322.       if (list.offsetWidth != list.parentNode.clientWidth - list.offsetLeft) {
  323.         // Set the width to the correct size. This causes the relayout.
  324.         list.style.width = list.parentNode.clientWidth - list.offsetLeft + 'px';
  325.         // Remove the temporary style.width in a timeout. Once the timer fires
  326.         // the size should not change since we already fixed the width.
  327.         window.setTimeout(function() {
  328.           list.style.width = '';
  329.         }, 0);
  330.       }
  331.     }
  332.   };
  333.  
  334.   /**
  335.    * The ID of the bookmark folder we are displaying.
  336.    * @type {string}
  337.    */
  338.   cr.defineProperty(BookmarkList, 'parentId', cr.PropertyKind.JS,
  339.                     function() {
  340.                       this.reload();
  341.                     });
  342.  
  343.   /**
  344.    * The contextMenu property.
  345.    * @type {cr.ui.Menu}
  346.    */
  347.   cr.ui.contextMenuHandler.addContextMenuProperty(BookmarkList);
  348.  
  349.   /**
  350.    * Creates a new bookmark list item.
  351.    * @param {!BookmarkTreeNode} bookmarkNode The bookmark node this represents.
  352.    * @constructor
  353.    * @extends {cr.ui.ListItem}
  354.    */
  355.   function BookmarkListItem(bookmarkNode) {
  356.     var el = cr.doc.createElement('div');
  357.     el.bookmarkNode = bookmarkNode;
  358.     BookmarkListItem.decorate(el);
  359.     return el;
  360.   }
  361.  
  362.   /**
  363.    * Decorates an element as a bookmark list item.
  364.    * @param {!HTMLElement} el The element to decorate.
  365.    */
  366.   BookmarkListItem.decorate = function(el) {
  367.     el.__proto__ = BookmarkListItem.prototype;
  368.     el.decorate();
  369.   };
  370.  
  371.   BookmarkListItem.prototype = {
  372.     __proto__: ListItem.prototype,
  373.  
  374.     /** @override */
  375.     decorate: function() {
  376.       ListItem.prototype.decorate.call(this);
  377.  
  378.       var bookmarkNode = this.bookmarkNode;
  379.  
  380.       this.draggable = true;
  381.  
  382.       var labelEl = this.ownerDocument.createElement('div');
  383.       labelEl.className = 'label';
  384.       labelEl.textContent = bookmarkNode.title;
  385.  
  386.       var urlEl = this.ownerDocument.createElement('div');
  387.       urlEl.className = 'url';
  388.  
  389.       if (bmm.isFolder(bookmarkNode)) {
  390.         this.className = 'folder';
  391.       } else {
  392.         labelEl.style.backgroundImage = url(getFaviconURL(bookmarkNode.url));
  393.         labelEl.style.backgroundSize = '16px';
  394.         urlEl.textContent = bookmarkNode.url;
  395.       }
  396.  
  397.       this.appendChild(labelEl);
  398.       this.appendChild(urlEl);
  399.  
  400.       // Initially the ContextMenuButton was added here but it slowed down
  401.       // rendering a lot so it is now added using mouseover.
  402.     },
  403.  
  404.     /**
  405.      * The ID of the bookmark folder we are currently showing or loading.
  406.      * @type {string}
  407.      */
  408.     get bookmarkId() {
  409.       return this.bookmarkNode.id;
  410.     },
  411.  
  412.     /**
  413.      * Whether the user is currently able to edit the list item.
  414.      * @type {boolean}
  415.      */
  416.     get editing() {
  417.       return this.hasAttribute('editing');
  418.     },
  419.     set editing(editing) {
  420.       var oldEditing = this.editing;
  421.       if (oldEditing == editing)
  422.         return;
  423.  
  424.       var url = this.bookmarkNode.url;
  425.       var title = this.bookmarkNode.title;
  426.       var isFolder = bmm.isFolder(this.bookmarkNode);
  427.       var listItem = this;
  428.       var labelEl = this.firstChild;
  429.       var urlEl = labelEl.nextSibling;
  430.       var labelInput, urlInput;
  431.  
  432.       // Handles enter and escape which trigger reset and commit respectively.
  433.       function handleKeydown(e) {
  434.         // Make sure that the tree does not handle the key.
  435.         e.stopPropagation();
  436.  
  437.         // Calling list.focus blurs the input which will stop editing the list
  438.         // item.
  439.         switch (e.keyIdentifier) {
  440.           case 'U+001B':  // Esc
  441.             labelInput.value = title;
  442.             if (!isFolder)
  443.               urlInput.value = url;
  444.             // fall through
  445.             cr.dispatchSimpleEvent(listItem, 'canceledit', true);
  446.           case 'Enter':
  447.             if (listItem.parentNode)
  448.               listItem.parentNode.focus();
  449.         }
  450.       }
  451.  
  452.       function handleBlur(e) {
  453.         // When the blur event happens we do not know who is getting focus so we
  454.         // delay this a bit since we want to know if the other input got focus
  455.         // before deciding if we should exit edit mode.
  456.         var doc = e.target.ownerDocument;
  457.         window.setTimeout(function() {
  458.           var activeElement = doc.activeElement;
  459.           if (activeElement != urlInput && activeElement != labelInput) {
  460.             listItem.editing = false;
  461.           }
  462.         }, 50);
  463.       }
  464.  
  465.       var doc = this.ownerDocument;
  466.       if (editing) {
  467.         this.setAttribute('editing', '');
  468.         this.draggable = false;
  469.  
  470.         labelInput = doc.createElement('input');
  471.         labelInput.placeholder =
  472.             loadTimeData.getString('name_input_placeholder');
  473.         replaceAllChildren(labelEl, labelInput);
  474.         labelInput.value = title;
  475.  
  476.         if (!isFolder) {
  477.           urlInput = doc.createElement('input');
  478.           urlInput.type = 'url';
  479.           urlInput.required = true;
  480.           urlInput.placeholder =
  481.               loadTimeData.getString('url_input_placeholder');
  482.  
  483.           // We also need a name for the input for the CSS to work.
  484.           urlInput.name = '-url-input-' + cr.createUid();
  485.           replaceAllChildren(urlEl, urlInput);
  486.           urlInput.value = url;
  487.         }
  488.  
  489.         function stopPropagation(e) {
  490.           e.stopPropagation();
  491.         }
  492.  
  493.         var eventsToStop =
  494.             ['mousedown', 'mouseup', 'contextmenu', 'dblclick', 'paste'];
  495.         eventsToStop.forEach(function(type) {
  496.           labelInput.addEventListener(type, stopPropagation);
  497.         });
  498.         labelInput.addEventListener('keydown', handleKeydown);
  499.         labelInput.addEventListener('blur', handleBlur);
  500.         cr.ui.limitInputWidth(labelInput, this, 100, 0.5);
  501.         labelInput.focus();
  502.         labelInput.select();
  503.  
  504.         if (!isFolder) {
  505.           eventsToStop.forEach(function(type) {
  506.             urlInput.addEventListener(type, stopPropagation);
  507.           });
  508.           urlInput.addEventListener('keydown', handleKeydown);
  509.           urlInput.addEventListener('blur', handleBlur);
  510.           cr.ui.limitInputWidth(urlInput, this, 200, 0.5);
  511.         }
  512.  
  513.       } else {
  514.         // Check that we have a valid URL and if not we do not change the
  515.         // editing mode.
  516.         if (!isFolder) {
  517.           var urlInput = this.querySelector('.url input');
  518.           var newUrl = urlInput.value;
  519.           if (!newUrl) {
  520.             cr.dispatchSimpleEvent(this, 'canceledit', true);
  521.             return;
  522.           }
  523.  
  524.           if (!urlInput.validity.valid) {
  525.             // WebKit does not do URL fix up so we manually test if prepending
  526.             // 'http://' would make the URL valid.
  527.             // https://bugs.webkit.org/show_bug.cgi?id=29235
  528.             urlInput.value = 'http://' + newUrl;
  529.             if (!urlInput.validity.valid) {
  530.               // still invalid
  531.               urlInput.value = newUrl;
  532.  
  533.               // In case the item was removed before getting here we should
  534.               // not alert.
  535.               if (listItem.parentNode) {
  536.                 // Select the item again.
  537.                 var dataModel = this.parentNode.dataModel;
  538.                 var index = dataModel.indexOf(this.bookmarkNode);
  539.                 var sm = this.parentNode.selectionModel;
  540.                 sm.selectedIndex = sm.leadIndex = sm.anchorIndex = index;
  541.  
  542.                 alert(loadTimeData.getString('invalid_url'));
  543.               }
  544.               urlInput.focus();
  545.               urlInput.select();
  546.               return;
  547.             }
  548.             newUrl = 'http://' + newUrl;
  549.           }
  550.           urlEl.textContent = this.bookmarkNode.url = newUrl;
  551.         }
  552.  
  553.         this.removeAttribute('editing');
  554.         this.draggable = true;
  555.  
  556.         labelInput = this.querySelector('.label input');
  557.         var newLabel = labelInput.value;
  558.         labelEl.textContent = this.bookmarkNode.title = newLabel;
  559.  
  560.         if (isFolder) {
  561.           if (newLabel != title) {
  562.             cr.dispatchSimpleEvent(this, 'rename', true);
  563.           }
  564.         } else if (newLabel != title || newUrl != url) {
  565.           cr.dispatchSimpleEvent(this, 'edit', true);
  566.         }
  567.       }
  568.     }
  569.   };
  570.  
  571.   return {
  572.     BookmarkList: BookmarkList,
  573.     list: list
  574.   };
  575. });
  576.